using System.Text;
using GuidCollisionWaiter.Hubs;
using GuidCollisionWaiter.Hubs.Interfaces;
using GuidCollisionWaiter.Models;
using Microsoft.AspNetCore.SignalR;

namespace GuidCollisionWaiter.BackgroundServices;

public class GuidGeneratorBackgroundService : BackgroundService
{
    private const string CollidedText = "COLLIDED!!!";
    private const string NotCollidedText = "No collision";

    private readonly IHubContext<GuidHub, IGuidHub> _guidsHub;
    private readonly TimeSpan _executionPeriod = TimeSpan.FromMilliseconds(300);

    private GuidsClosestToCollideResult? _closestToCollideResult;

    public GuidGeneratorBackgroundService(IHubContext<GuidHub, IGuidHub> guidsHub)
    {
        _guidsHub = guidsHub;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var startDate = DateTime.Now;
        var sincePostfix = " since: " + startDate.ToString("dd/MM/yyyy HH:mm:ss");

        while (!stoppingToken.IsCancellationRequested)
        {
            await Task.Delay(_executionPeriod, stoppingToken);

            var firstGuid = Guid.NewGuid();
            var secondGuid = Guid.NewGuid();

            var isItReallyACollision = firstGuid == secondGuid;

            if (!isItReallyACollision)
            {
                UpdateNewClosestResultIfSo(firstGuid, secondGuid);
            }

            var resultText = isItReallyACollision
                ? CollidedText
                : NotCollidedText + sincePostfix + $" ({Math.Floor((DateTime.Now - startDate).TotalDays)} days)";

            var response = new GuidsCollisionCheckResult(firstGuid,
                secondGuid, resultText,
                isItReallyACollision, _closestToCollideResult!
            );

            await _guidsHub.Clients.All.CollisionChecked(response);

            if (!isItReallyACollision)
                continue;

            await WriteCollisionToFile(firstGuid, secondGuid, stoppingToken);
            return;
        }
    }

    private void UpdateNewClosestResultIfSo(Guid first, Guid second)
    {
        var equalFirstCharsCount = 0;

        var firstStr = first.ToString();
        var secondStr = second.ToString();

        for (var i = 0; i < firstStr.Length; i++)
        {
            if (firstStr[i] != secondStr[i])
                break;

            equalFirstCharsCount++;
        }

        if (_closestToCollideResult is null
            || equalFirstCharsCount > _closestToCollideResult.EqualFirstCharactersCount)
        {
            _closestToCollideResult = new GuidsClosestToCollideResult(first, second, equalFirstCharsCount);
        }
    }

    private static async Task WriteCollisionToFile(Guid firstGuid, Guid secondGuid, CancellationToken stoppingToken)
    {
        var collisionLogPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "collision.txt";

        await using var fileStream = File.Create(collisionLogPath);

        var collisionLogText = DateTime.Now + ": " + firstGuid + " " + secondGuid;
        await fileStream.WriteAsync(Encoding.UTF8.GetBytes(collisionLogText), stoppingToken);
    }
}